using System;
using System.Collections.Generic;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;
using System.Text;
using System.IO;

namespace TBOCrypto
{
    public class XMLSupp
    {
        private const string ENCRYPTED_DATA_NODE = "EncryptedData";
        private System.Security.Cryptography.RijndaelManaged aes;
        private const string OID_SHA1 = "1.3.14.3.2.26";


        public XMLSupp()
        {
            aes = new RijndaelManaged();
        }

        public XmlDocument LoadXmlFile(string path)
        {
            XmlDocument xmlDoc = new XmlDocument();

            FileStream fs = new FileStream(path, System.IO.FileMode.Open);
            xmlDoc.Load(fs);

            fs.Close();
            return xmlDoc;
        }

        private string GetDirPath(string filePath)
        {
            return filePath.Substring(0,filePath.LastIndexOf('\\') + 1);
        }

        private string GetFileName(string filePath)
        {
            return filePath.Substring(filePath.LastIndexOf('\\') + 1, (filePath.Length - 1 - filePath.LastIndexOf('\\')));
        }

        public void SymetricEncode(string filePath, string node)
        {
            XmlDocument xmlDoc = LoadXmlFile(filePath);
            XmlElement elem = xmlDoc.GetElementsByTagName(node)[0] as XmlElement;

            EncryptedXml eXml = new EncryptedXml(LoadXmlFile(filePath));
            byte[] data = eXml.EncryptData(elem, aes, true);

            EncryptedData enData = new EncryptedData();
            enData.Type = EncryptedXml.XmlEncElementUrl;
            enData.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES128Url);
            enData.CipherData.CipherValue = data;

            EncryptedXml.ReplaceElement(elem, enData, true);

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "EncSym" + file, System.IO.FileMode.Create))
            {
                xmlDoc.Save(fs);
            }
        }
        public void SymetricDecode(string filePath)
        {
            XmlDocument doc = LoadXmlFile(filePath);
            XmlElement encryptedElem = doc.GetElementsByTagName(ENCRYPTED_DATA_NODE)[0] as XmlElement;

            EncryptedData enData = new EncryptedData();
            enData.LoadXml(encryptedElem);
            EncryptedXml eXml = new EncryptedXml();

            byte[] data = eXml.DecryptData(enData, aes);
            eXml.ReplaceData(encryptedElem, data);

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "Dec" + file, System.IO.FileMode.Create))
            {
                doc.Save(fs);
            }
        }

        public void AsymetricEncode(string filePath, string node)
        {
            XmlDocument xmlDoc = LoadXmlFile(filePath);
            CspParameters csp = new CspParameters();
            csp.KeyContainerName = "KeysContainer";
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

            XmlElement elem = xmlDoc.GetElementsByTagName(node)[0] as XmlElement;
            EncryptedXml eXml = new EncryptedXml();
            byte[] data = eXml.EncryptData(elem, aes, true);
            EncryptedData enData = new EncryptedData();
            enData.Type = EncryptedXml.XmlEncElementUrl;
            enData.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncAES128Url);

            /*
             *  Add and encrypt key
             */
            EncryptedKey ek = new EncryptedKey();
            byte[] encKey = EncryptedXml.EncryptKey(aes.Key, rsa, false);
            ek.CipherData = new CipherData(encKey);
            ek.EncryptionMethod = new EncryptionMethod(EncryptedXml.XmlEncRSA15Url);
            enData.KeyInfo.AddClause(new KeyInfoEncryptedKey(ek));

            /*
             *  Add public key identifier
             */
            KeyInfoName kin = new KeyInfoName();
            kin.Value = "PublicKeyIdentifier";
            ek.KeyInfo.AddClause(kin);

            /*
             *  Encrypt
             */
            enData.CipherData.CipherValue = data;
            EncryptedXml.ReplaceElement(elem, enData, true);

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "EncAsym" + file, FileMode.Create))
            {
                xmlDoc.Save(fs);
            }
        }
        public void AsymetricDecode(string filePath)
        {
            XmlDocument encDoc = LoadXmlFile(filePath);
            CspParameters csp = new CspParameters();
            csp.KeyContainerName = "KeysContainer";
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(csp);

            EncryptedXml exml = new EncryptedXml(encDoc);
            exml.AddKeyNameMapping("PublicKeyIdentifier", rsa);
            exml.DecryptDocument();

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "Dec" + file, FileMode.Create))
            {
                encDoc.Save(fs);
            }
        }

        public void Encode509(string filePath, string node)
        {
            XmlDocument doc = LoadXmlFile(filePath);
            XmlElement elem = doc.GetElementsByTagName(node)[0] as XmlElement;

            EncryptedXml eXml = new EncryptedXml(doc);
            EncryptedData enData = eXml.Encrypt(elem, CertsMan.GetMyCertificate());
            EncryptedXml.ReplaceElement(elem, enData, false);

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "X509Enc" + file, FileMode.Create))
            {
                doc.Save(fs);
            }
        }
        public void Decode509(string filePath)
        {
            XmlDocument doc = LoadXmlFile(filePath);
            EncryptedXml eXml = new EncryptedXml(doc);
            eXml.DecryptDocument();

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "Dec" + file, FileMode.Create))
            {
                doc.Save(fs);
            }
        }

        public void SignXML(string filePath)
        {
            XmlDocument doc = LoadXmlFile(filePath);

            
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            doc.PreserveWhitespace = false;
            SignedXml sgn = new SignedXml(doc);
            sgn.SigningKey = rsa;
            sgn.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl;

            
            Reference reference = new Reference();
            XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
            XmlDsigExcC14NTransform xmlExcMet = sgn.SignedInfo.CanonicalizationMethodObject as XmlDsigExcC14NTransform;

            reference.Uri = "#signme";
            reference.AddTransform(xmlExcMet);
            reference.AddTransform(env);
            sgn.AddReference(reference);

            
            KeyInfo keyInfo = new KeyInfo();
            keyInfo.AddClause(new RSAKeyValue(rsa));
            sgn.KeyInfo = keyInfo;
            sgn.ComputeSignature();

            XmlElement xmlDgSgn = sgn.GetXml();
            doc.DocumentElement.AppendChild(doc.ImportNode(xmlDgSgn, true));

            string dir = GetDirPath(filePath);
            string file = GetFileName(filePath);
            using (FileStream fs = new FileStream(dir + "Sgn" + file, FileMode.Create))
            {
                doc.Save(fs);
            }
        }

        public static byte[] ComputeDigest(string message)
        {
            Encoding en = Encoding.Unicode;
            HashAlgorithm hash = MD5.Create();
            return hash.ComputeHash(en.GetBytes(message));
        }

        public static bool VerifyDigest(string message, byte[] digest)
        {
            byte[] oldDig = ComputeDigest(message);
            if (CheckBytes(oldDig, digest))
                return true;
            else
                return false;
        }

        private static bool CheckBytes(byte[] one, byte[] two)
        {
            for (int i = 0; i < one.Length; i++)
            {
                if (one[i] != two[i])
                    return false;
            }

            return true;
        }

        private byte[] GenerateRSASignature(byte[] data,
        RSAParameters key)
        {
            HashAlgorithm hash = SHA1.Create();
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.ImportParameters(key);
            byte[] hashValue = hash.ComputeHash(data);
            byte[] signature = rsa.SignHash(hashValue, OID_SHA1);
            return signature;
        }

        public static string GenerateHMAC(string message, string pass)
        {
            // Instantiate the HMAC class
            HMACSHA256 hmac = new HMACSHA256();

            hmac.Key = ASCIIEncoding.ASCII.GetBytes(pass);

            return ASCIIEncoding.ASCII.GetString(hmac.ComputeHash(ASCIIEncoding.ASCII.GetBytes(message)));
        }

        public static bool ValidateHMAC(string message, string pass, string oldHmac)
        {
            if (oldHmac == GenerateHMAC(message, pass))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
